/**
 *
 */
package 线程.queue;

import lombok.EqualsAndHashCode;

import java.util.Objects;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 * DelayQueue是一个无界阻塞队列，只有在延迟期满时才能从中提取元素。该队列的头部是延迟期满后保存时间最长的Delayed 元素。
 * DelayQueue阻塞队列在我们系统开发中也常常会用到，例如：缓存系统的设计，缓存中的对象，超过了空闲时间，需要从缓存中移出；
 * 任务调度系统，能够准确的把握任务的执行时间。我们可能需要通过线程处理很多时间上要求很严格的数据，如果使用普通的线程，
 * 我们就需要遍历所有的对象，一个一个的检查看数据是否过期等，首先这样在执行上的效率不会太高，
 * 其次就是这种设计的风格也大大的影响了数据的精度。一个需要12:00点执行的任务可能12:01才执行,
 * 这样对数据要求很高的系统有更大的弊端。由此我们可以使用DelayQueue。
 *
 * @Title:
 * @author zhoujie
 * @date 2018年6月25日 上午10:07:10
 *
 */
public class DelayQueueDemo {
    public static void main(String[] args) {
        DelayQueue<MyDelayQueue> queues = new DelayQueue<>();
        queues.put(new MyDelayQueue("sky", getRanTime()));
        queues.put(new MyDelayQueue("张三", getRanTime()));
        queues.put(new MyDelayQueue("李四", getRanTime()));
        queues.put(new MyDelayQueue("王五", getRanTime()));

        for (; ; ) {
            MyDelayQueue mq = queues.peek();// 一直找指到找到一个为止，poll 只找一次 结合compareTo
            // 效率高
            if (Objects.nonNull(mq)) {
                System.out.println(mq.getName() + " 已经完成任务 消耗时间(s)：" + (mq.getTime() / (365 * 24 * 3600 * 1000)));
                queues.remove(mq);
                if (queues.size() == 0) {
                    break;
                }
            }
        }
        System.out.println("所有任务执行完毕");
    }

    /**
     * 返回1到60秒的随机时间
     *
     * @return
     */
    public static long getRanTime() {
        return (System.currentTimeMillis() + (int) ((Math.random() * 6 + 1) * 1000));
    }
}

/**
 * @Title:
 * @author zhoujie
 * @date 2018年6月25日 上午10:37:57
 *
 */
@EqualsAndHashCode
class MyDelayQueue implements Delayed {

    public MyDelayQueue(String name, long time) {
        this.name = name;
        this.time = time;
    }

    private String name;
    /**
     * 到期时间
     */
    /**
     *
     */
    private Long time;

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * @param name
     *            the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @return the time
     */
    public Long getTime() {
        return time;
    }

    /**
     * @param time
     *            the time to set
     */
    public void setTime(Long time) {
        this.time = time;
    }

    @Override
    public int compareTo(Delayed o) {
        if (o == null || !(o instanceof MyDelayQueue)) {
            return 1;
        }
        if (o == this) {
            return 0;
        }
        MyDelayQueue m = (MyDelayQueue) o;
        return this.getTime().compareTo(m.getTime());
    }

    @Override
    public long getDelay(TimeUnit unit) {
        return time - System.currentTimeMillis();
    }
}
